Obed Owusu
Zero-trust · Malware scanning · Time-limited access

SecureShare Hub. Azure native file sharing with enforced sign-in & content scanning.

A hardened blueprint for distributing files from fully private Azure Blob Storage using Easy Auth–enforced Microsoft sign-in, short-lived user delegation SAS URLs, and an event-driven malware scanning pipeline. Every download is authenticated, time-bounded, and auditable from code to storage.

Azure Functions · Python Terraform IaC GitHub Actions · OIDC Easy Auth · Entra ID Blob Storage (private only) Event Grid malware pipeline
Engineering deep dive (code · IaC · KQL) →
Access model: private Blob + user delegation SAS only SAS lifetime: typically 15–30 minutes CI/CD: passwordless GitHub Actions via OIDC
Architecture diagram for SecureShare Hub
View full project on GitHub
SecureShare Hub – control surface
Zero public URLs No stored keys
Architecture snapshot Full diagram in gallery below.
GitHub Actions → Terraform → Function App
Easy Auth (Entra ID) → Managed Identity
Upload → incoming-raw → Event Grid → Scan Function
→ safe-files / quarantine → SAS → Client download

What SecureShare Hub solves

Many teams still share sensitive files using public blob URLs, long-lived SAS tokens, or emailed attachments. SecureShare Hub shows how to do it properly on Azure: enforce Microsoft sign-in for every request, keep storage completely private, scan uploads before promotion, and issue only short-lived read-only links.

The pattern

Zero-trust file distribution, end-to-end.

Files never live in public containers. Users never see storage keys or connection strings. Every download passes through identity, authorization, and an audited SAS-issuance step, while an event-driven pipeline scans new uploads before they become eligible for sharing.

  • Storage accounts created with "allow_blob_public_access = false".
  • Easy Auth + Entra ID forces sign-in for HTML and APIs – there is no anonymous entry point.
  • Uploads land in an untrusted "incoming-raw" container, then flow through a scan Function.
  • Only clean files are promoted into "safe-files"; suspicious content is diverted to "quarantine" and cleaned up automatically.
  • Read-only user delegation SAS URLs are created per request, for a tight expiry window.
Key outcomes

Secure by default, auditable by design.

The platform is built as a realistic reference implementation for Azure-native secure sharing. It also doubles as a portfolio project demonstrating IaC, automation, identity, and security controls working together.

Enforced Microsoft sign-in (Easy Auth) Private Blob Storage only Event Grid malware pipeline Versioning + soft delete Lifecycle policies (cost + retention) GZRS storage redundancy Passwordless CI/CD with OIDC

Zero Trust by Design

SecureShare Hub uses private storage, enforced sign-in, short-lived links, and a full content-safety pipeline. Every part of the system follows zero-trust principles—treat the network as untrusted, keep data hidden, rely on identity and policy, and reduce the blast radius of any token or file.

Zero-trust principle
Private by default

Storage accounts disable blob public access at the account level. All Blob containers are private, and the only valid access path is via the hardened Function backend issuing user delegation SAS URLs.

Private Blob Storage RBAC + managed identity
Zero-trust principle
Identity-driven access

App Service Authentication (Easy Auth) with Entra ID is turned on for the entire Function App. Any unauthenticated request is redirected to Microsoft login – there is no public HTML and no anonymous API surface.

The backend reads X-MS-CLIENT-PRINCIPAL and related headers to identify the caller and derive roles and policies.

Easy Auth enforced Entra ID tokens only
Zero-trust principle
Time-limited, least-privilege links

The Function uses the Storage SDK with a user delegation key to mint read-only SAS tokens that are scoped to a single blob and expire after a short window (typically 15–30 minutes). After that, even a leaked URL is useless.

Read-only SAS Per-file scope Tight expiry
Content safety
Event-driven malware scanning

Uploads enter an incoming-raw container. Azure Event Grid watches for new blobs and triggers a dedicated Scan Function. That Function downloads the content, runs AV checks, and either promotes the file into safe-files or moves it into quarantine with metadata scanStatus set accordingly.

incoming-raw → safe-files/quarantine Event Grid trigger
Resilience & lifecycle
Versioning, soft delete, and DR

Blob versioning and 30-day soft delete are enabled on the Storage Account, with GZRS redundancy for zone-plus-region resilience. Management policies move older blobs to Cool tier and eventually delete them, while quarantine data is cleaned up after a short retention window.

Versioning + soft delete Lifecycle to Cool + delete GZRS replication
Platform feature
IaC + passwordless CI/CD

Terraform modules create the resource group, Function App, Storage, Key Vault, Event Grid topics, and role assignments. GitHub Actions deploys the stack using OpenID Connect federation, so there are no long-lived client secrets in pipeline configuration.

Terraform modules GitHub Actions + OIDC No stored SP keys

From Commit to Secure Download.

SecureShare Hub uses nine tightly integrated components to move a file from upload to scan to signed download. Every part of the architecture maps directly to a real Azure service and Terraform module, making the system fully rebuildable end to end.

Logical components

  1. 1
    GitHub Actions CI/CD
    deploy-infrastructure · deploy-application
    Terraform and the Python Function are deployed through OIDC-based workflows, avoiding stored service principal secrets.
    OIDC login Separate infra / app stages
  2. 2
    Terraform IaC layer
    Resource Group · Storage · Function App · Key Vault · Event Grid
    Opinionated Terraform modules enforce private storage, managed identities, minimum RBAC roles, and storage lifecycle rules.
  3. 3
    Function App with Easy Auth
    App Service Authentication + Entra ID
    App Service Authentication is set to "On" and "Require authentication". Any unauthenticated request is redirected to Microsoft login before HTML or APIs are reachable.
    X-MS-CLIENT-PRINCIPAL Enforced sign-in
  4. 4
    Python API backend
    SAS issuance & authorization checks
    A Python Function handles authenticated requests, derives identity from platform headers, and issues user delegation SAS URLs only for authorized users and clean files.
  5. 5
    Azure Key Vault
    Non-identity secrets only
    Key Vault stores app configuration and integration secrets. Storage access relies on managed identity + RBAC instead of connection strings.
  6. 6
    Blob Storage account
    incoming-raw · safe-files · quarantine
    Storage accounts are private, with three containers: untrusted uploads, clean files, and quarantined items. Versioning, soft delete, and lifecycle policies are configured via Terraform.
  7. 7
    Event Grid + Scan Function
    BlobCreated → scan → promote / quarantine
    Event Grid subscribes to BlobCreated events in incoming-raw and triggers a scanning Function that decides whether to promote the file to safe-files or move it to quarantine.
  8. 8
    User delegation SAS generator
    Per-file, read-only download links
    SAS URLs are built using the Function's managed identity and user delegation keys, with time-boxed permissions and audit logging.
  9. 9
    Client applications
    Portal / internal tools / external clients
    Any authenticated Entra ID user can hit the Function endpoint. The "client" may be a web portal, an internal tool, or a partner application that exchanges SAS links behind the scenes.

End-to-end workflow (high-level)

  1. 1
    Developer pushes to GitHub
    Code changes (Terraform, Functions, or configuration) land in the main repo.
  2. 2
    CI/CD deploys infra & app
    GitHub Actions authenticates into Azure using OIDC, runs Terraform apply, and then publishes the Function App.
  3. 3
    User hits SecureShare Hub URL
    Because App Service Authentication is enabled and required, the user is redirected to login.microsoftonline.com and must sign in with their Microsoft 365 account.
  4. 4
    Upload into incoming-raw
    The front-end or a back-office process uploads files into the untrusted incoming-raw container using the backend.
  5. 5
    Event-driven malware scan
    Event Grid emits a BlobCreated event, which triggers the Scan Function to download and scan the file.
  6. 6
    Promote or quarantine
    Clean files are copied into safe-files with metadata scanStatus=clean. Suspicious files are moved into quarantine and tagged accordingly.
  7. 7
    User requests a download
    An authenticated user calls the Function to get a secure link for a given file.
  8. 8
    Backend issues SAS (if safe)
    The Function resolves identity, checks authorization, verifies scanStatus=clean on the blob, and then issues a short-lived read-only user delegation SAS URL.
  9. 9
    Client downloads directly from Storage
    The browser or client streams the file directly from Blob Storage using the SAS URL. After the expiry window, the link automatically stops working.

Defense-in-Depth Architecture.

SecureShare Hub is built to meet strict security expectations, layering protection across identity, storage, automation, and content safety. No public storage, no long-lived secrets, and no unscanned files, every layer reduces unnecessary trust and stands up to serious security scrutiny.

Storage hardening
  • Blob public access is disabled on the Storage Account; all containers are private.
  • Three-container pattern: "incoming-raw" (untrusted), "safe-files" (clean only), "quarantine" (suspicious).
  • Blob versioning and 30-day soft delete protect against overwrite / delete mistakes.
  • Lifecycle policies move long-lived content to Cool tier and delete it after retention, while quarantine data is purged sooner.
Identity-first access
  • App Service Authentication (Easy Auth) with Entra ID is configured to require authentication for all routes.
  • Any unauthenticated request is redirected to Microsoft login; there's no anonymous HTML or Function endpoint.
  • The backend derives identity from "X-MS-CLIENT-PRINCIPAL" rather than trusting client-supplied identifiers.
  • Managed identity + Storage RBAC replace shared keys and connection strings.
Malware & content safety
  • Uploads are treated as untrusted input and stored only in "incoming-raw".
  • Event Grid feeds a Scan Function that inspects the content and assigns "scanStatus" metadata.
  • The SAS generation Function only ever targets "safe-files" and checks for "scanStatus=clean" before issuing a link.
  • Suspicious content is isolated in "quarantine" and pruned automatically by lifecycle rules.
Secrets & CI/CD
  • Key Vault stores non-identity configuration secrets with tightly scoped access policies.
  • Storage access avoids connection strings and account keys entirely, relying on managed identity.
  • GitHub Actions uses OIDC federation to log in to Azure; no client secret is stored in the repo.
  • Terraform state and deployments are repeatable, traceable, and environment-aware.
Future evolution

With the foundations in place (Easy Auth, private storage, scanning pipeline, lifecycle, and DR), future iterations can focus on authorization depth and multi-tenant capabilities:

  • Integrate Entra ID groups / roles for fine-grained per-folder permissions.
  • Use private endpoints and Azure Firewall / Application Gateway for additional network isolation.
  • Stream logs into Log Analytics + Sentinel with alert rules for unusual download activity.
  • Expose an admin UI for revoking links, reviewing quarantine, and auditing access.

How SecureShare Works

SecureShare Hub lets users sign in, upload a file, watch it get scanned instantly, and receive a short-lived secure link—all without the file ever being exposed public

1
Sign in with Microsoft 365

The user hits the SecureShare Hub URL and is immediately redirected to Microsoft login. No guest, anonymous, or link-only access is possible.

2
Upload into incoming-raw

The web UI or a back-office tool uploads files into the incoming-raw container via the authenticated Function backend. At this point the file is untrusted and not yet shareable.

3
Automatic malware scanning

Event Grid detects the BlobCreated event and triggers the Scan Function, which downloads the file, runs AV checks, and promotes it to safe-files or moves to quarantine.

4
Request a secure download link

When the file is clean, the user requests a link. The backend validates identity, checks scanStatus=clean, and issues a short-lived, per-file user delegation SAS URL for direct download from Blob Storage.

Azure-native, IaC-driven, and CI/CD-first from day one.

SecureShare Hub follows a true production, first approach Azure native services, identity aware Functions, private storage, event-driven processing and fully repeatable infrastructure deployed through Terraform and CI/CD from day one.

Core platform
  • Azure Functions (Python) for SAS issuance and malware scanning.
  • Azure Blob Storage with private access, versioning, and lifecycle policies.
  • Azure Event Grid for BlobCreated → Scan Function orchestration.
  • Azure Key Vault for configuration secrets (non-storage identity).
Identity, security & networking
  • Microsoft Entra ID + App Service Authentication (Easy Auth).
  • Managed identity + RBAC for storage access (no connection strings).
  • Short-lived user delegation SAS for per-file, read-only access.
  • Designed to extend with private endpoints, Firewall, and Sentinel.
DevOps & observability
  • Terraform for resource groups, storage, Functions, Key Vault, Event Grid.
  • GitHub Actions with OIDC (passwordless Azure login).
  • Split infra/app workflows for safer deployments.
  • Log Analytics / App Insights for SAS issuance and download audit trails.

SecureShare Hub

A zero-trust, scan-first Azure file sharing pattern with enforced Microsoft sign-in, private-only Blob Storage, short-lived user delegation SAS URLs, and Terraform driven lifecycle, DR, and CI/CD.

Zero trust Azure Python Azure Functions Terraform IaC GitHub Actions + OIDC Easy Auth (Entra ID) Event Grid malware pipeline
Access model
Private Blob + per-file user delegation SAS only
SAS lifetime
Configurable, typically 15–30 minutes
Authentication
Easy Auth (Entra ID), sign-in required for all requests
Content safety
incoming-raw → Event Grid → Scan Function → safe-files/quarantine
Resilience
Blob versioning, soft delete, lifecycle policies, GZRS
CI/CD & IaC
Terraform modules + GitHub Actions with OIDC federation
Jump to full engineering deep dive →

What I Learned Building SecureShare Hub.

This section highlights the real challenges, trade-offs, and fixes I encountered throughout the project and what I would confidently repeat in future builds..

Designing the three container model without over complicating flows

A naive design would put everything into a single container with a "scanStatus" flag. That makes it too easy for misconfigurations or bugs to expose unscanned files.

  • Solution: separate "incoming-raw", "safe-files", and "quarantine" at the container level.
  • Benefit: simplifies access rules – SAS issuance Functions only ever look at "safe files".
  • Trade-off: slightly more storage operations (copy/move), but much clearer security boundaries.
Getting Easy Auth + Entra ID headers correct for identity-driven logic

Easy Auth abstracts token validation but exposes identity through headers like "X-MS-CLIENT-PRINCIPAL". Handling this safely is not obvious at first.

  • Challenge: decoding the principal header and avoiding trusting client-supplied IDs.
  • Solution: central helper in the Function code that parses, validates, and normalizes the identity object.
  • Outcome: authorization checks are expressed in terms of roles / groups, not raw IDs from the query string.
Balancing tight SAS expiry with usability

Very short SAS lifetimes are more secure, but can frustrate users if downloads are slow or they share a link just before expiry.

  • Experimented with lifetimes between 5 and 60 minutes.
  • Settled on a default of 15–30 minutes, with room for policy-based adjustments.
  • Idea for future: add a “re-issue link” button in the UI with extra logging.
Keeping CI/CD secure without making it painful

Using client secrets in GitHub Actions would have been the easiest path, but it contradicts the project's zero-trust goals.

  • Solution: use OpenID Connect (OIDC) federation for GitHub → Azure login.
  • Benefit: no long-lived credentials in the repo or pipeline.
  • Lesson: infrastructure pipelines should follow the same security principles as application code.